package com.chenyun.boot.web.aspect;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.chenyun.common.annotation.Log;
import com.chenyun.common.context.SpringContext;
import com.chenyun.common.service.SysLogFeign;
import com.chenyun.common.utils.IpUtils;
import com.chenyun.common.utils.SpelUtil;
import com.chenyun.common.utils.ThreadUtil;
import com.chenyun.common.vo.AjaxResult;
import com.chenyun.common.vo.OperLog;
import com.chenyun.common.vo.PageResult;
import com.chenyun.common.vo.SessionUser;
import com.chenyun.satoken.starter.util.SaUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.NamedThreadLocal;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.util.Date;

/**
 * 审计日志的切面
 *
 * @author Lonni
 * @date 2022-10-31 21:00
 */
@Component
@Slf4j
@Aspect
@Order(30)
public class LogAspect implements Serializable {

    /**
     * 记录当前线程进入的时间
     */
    private static final ThreadLocal<Date> BEGIN_TIME_THREAD_LOCAL = new NamedThreadLocal<>("SYSTEM-LOG");

    @Autowired
    private HttpServletRequest httpServletRequest;

    @Resource
    private SysLogFeign sysLogFeign;



    @Pointcut("@annotation(com.chenyun.common.annotation.Log)")
    public void controllerAspect() {
    }

    @Before("controllerAspect()")
    public void before() {
        BEGIN_TIME_THREAD_LOCAL.set(new Date());
        log.info("进入日志切面-----");
    }

    /**
     * 后置通知,(在方法执行之后并返回数据) 用于拦截Controller层无异常的操作
     *
     * @param joinPoint
     * @param rvt
     */
    @AfterReturning(pointcut = "controllerAspect()", returning = "rvt")
    public void after(JoinPoint joinPoint, Object rvt) {
        try {
            log.info("开始记录操作日志");
            long endTime = System.currentTimeMillis();
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Log logAttr = signature.getMethod().getAnnotation(Log.class);
            String desc = logAttr.desc();
            if(StrUtil.isNotBlank(desc)){
                desc = SpelUtil.me.converSpEl(desc, joinPoint);
            }
            final SessionUser user = SaUtil.getLoginUser();
            //请求开始时间
            long beginTime = BEGIN_TIME_THREAD_LOCAL.get().getTime();
            //系统名称
            final String appName = SpringContext.getProperty("spring.application.name");
            //请求耗时
            Long usedTime = endTime - beginTime;
            OperLog operLog = OperLog.builder()
                    .title(logAttr.title())
                    .method(signature.getName())
                    .requestMethod(httpServletRequest.getMethod().toUpperCase())
                    .operUrl(httpServletRequest.getRequestURI())
                    .businessType(logAttr.businessType().name().toString())
                    .operIp(IpUtils.getIpAddr(httpServletRequest))
                    .operName(user == null ? "system" : user.getUserId())
                    .operatorType(logAttr.type().getCode())
                    .operParam(JSONUtil.toJsonStr(httpServletRequest.getParameterMap()))
                    .operDesc(desc)
                    .applicationName(appName)
                    .costTime(usedTime.intValue())
                    .build();
            log.info("日志信息:{}",operLog);
            //拿到执行结果
            int status = 0;
            String msg = "";
            if (rvt instanceof AjaxResult) {
                AjaxResult res = (AjaxResult) rvt;
                status = res.isSuccess() ? 0 : 1;
                msg = res.getMessage();
            } else if (rvt instanceof PageResult) {
                PageResult res = (PageResult) rvt;
                status = res.isSuccess() ? 0 : 1;
                msg = res.getMessage();
            } else {
                if (rvt instanceof Throwable) {
                    status = 1;
                    msg = ((Throwable) rvt).getLocalizedMessage();
                }
            }
            operLog.setStatus(String.valueOf(status));
            operLog.setErrorMsg(msg);
//            String s = TenantContext.me.tenantId();
//            System.out.println("获取租户id:"+s);
            //使用线程池发送保存信息
            ThreadUtil.getInstance().execute(() -> {
//                String s1 = TenantContext.me.tenantId();
//                System.out.println("获取租户id:"+s1);
                sysLogFeign.addOperLog(operLog);
            });
        } catch (Exception exception) {
            log.error("执行审计日志切面异常:{}", exception.getMessage());
        } finally {
            BEGIN_TIME_THREAD_LOCAL.remove();
        }

    }


}
